#!/usr/bin/env python
# -*- coding: utf-8 -*-
#

import os,os.path,tempfile,time

from bisect import insort

from bsddb import db, btopen

try:
    import cPickle
    pickle = cPickle
except ImportError:
    import pickle

try :
    from hashlib import md5 as MD5
except :
    from md5 import md5 as MD5

PATH = os.sep.join((tempfile.gettempdir(), 'dbtables/hello'))

TABLE = 'hellotbl'

def logging(fmt, *args) : print fmt % args


class   TestDBTables_2(object) :
    def setUp(self) :
        logging('%s setUp', self.__class__.__name__)

        if os.path.exists(PATH) :
           self.removedirs(PATH)

        os.makedirs(PATH, 0777)

        self._db_filename = os.sep.join((PATH, 'tabletest.db'))
        self._page_size = 8192
        self._cache_size = 1048576

        dbtype       = db.DB_BTREE  # must be set in derived class
        dbopenflags  = 0
        dbsetflags   = db.DB_DUP
        dbmode       = 0660
        dbname       = None

        self._dbh = None
        self._dbh = db.DB()
        self._dbh.set_flags(dbsetflags)

        self._dbh.open(self._db_filename,   
            mode = dbmode,
            dbtype = dbtype,
            flags = dbopenflags|db.DB_CREATE)

        ## setUp()

    def tearDown(self) :
        logging('%s tearDown', self.__class__.__name__)
        if self._dbh :
            self._dbh.sync()
            self._dbh.close()
            self._dbh = None
        ## tearDown()

    def removedirs(self, path) :
        items = os.listdir(path)
        for name in items :
            fullpath = os.sep.join((path, name))
            if os.path.isdir(fullpath) :
                self.removedirs(fullpath)
            else :
                os.remove(fullpath)

        os.rmdir(path)
        ## removedirs


    def run(self) :
        logging('%s run', self.__class__.__name__)

        for test_func in (self.test01_simple, self.test02_tps, self.test04_dupkey) :
            self.setUp()
            try :
                test_func()
            finally :
                self.tearDown()

        ## run()

    ###################################################################

    def test01_simple(self) :

        key = 'hello'
        # column : timestamp, ts, op, fileno, offset, length
        value = [ 12345678, 0, 0, 0, 5 ]
        data = ','.join([str(x) for x in value])
        self._dbh.put(key, data)

        key = 'world'
        # column : timestamp, ts, op, fileno, offset, length
        value = [ 12345678, 0, 0, 5, 5 ]
        data = ','.join([str(x) for x in value])
        self._dbh.put(key, data)

        _data = self._dbh.get( key )
        assert data == _data

        self._dbh.delete(key)

        _data = self._dbh.get( key )
        assert not _data

        ## test01_simple()


    def test02_tps(self) :
        logging('test performance ...')

        fileno = 0

        timestamp = int(time.time() * 1000)
        offset = 0
        length = 11

        time_start = time.time()
        times = 100000
        for i in xrange(times) :
            key = MD5(str(i)).hexdigest()
            timestamp += 1
            offset += length
            length += 11 + i

            value = [ timestamp, 0, fileno, offset, length ]
            data = ','.join([str(x) for x in value])
            self._dbh.put(key, data)

            if (i > 0) and (0 == (i % 10000)) :
                time_last = time.time() - time_start
                logging(' - test %d times last %.2f seconds ...', i, time_last)
                
        time_last = time.time() - time_start
        tps = times / time_last
        logging('test %d times last %.2f seconds, tps=%.2f', times, time_last, tps)


        print 'test cursor performance ...'
        time_start = time.time()
        times = 10000
        for i in xrange(times) :
            c = self._dbh.cursor()
            r = c.set(key)
            assert c.count() > 0
            while r :
                r = c.next()
            c.close()
            c = None

            if (i > 0) and (0 == (i % 1000)) :
                time_last = time.time() - time_start
                logging(' - test %d times last %.2f seconds ...', i, time_last)
        time_last = time.time() - time_start
        tps = times / time_last
        print ' - cursor %d times last %.2f seconds, tps=%.2f' % (times, time_last, tps)

        print 'test cursor performance with decode ...'
        time_start = time.time()
        times = 10000
        for i in xrange(times) :
            c = self._dbh.cursor()
            r = c.set(key)
            assert c.count() > 0
            while r :
                k,v = r
                t = v.split(',')
                x = [ int(x) for x in t ]

                r = c.next()
            c.close()
            c = None

            if (i > 0) and (0 == (i % 1000)) :
                time_last = time.time() - time_start
                logging(' - test %d times last %.2f seconds ...', i, time_last)
        time_last = time.time() - time_start
        tps = times / time_last
        print ' - cursor %d times last %.2f seconds, tps=%.2f' % (times, time_last, tps)

        ## test02_tps()

    def test03_tps(self) :
        logging('test performance ...')

        fileno = 0

        timestamp = int(time.time() * 1000)
        offset = 0
        length = 11

        records = []
        time_start = time.time()
        times = 100000
        for i in xrange(times) :
            key = MD5(str(i)).hexdigest()
            timestamp += 1
            offset += length
            length += 11 + i

            value = [ timestamp, 0, fileno, offset, length ]
            data = ','.join([str(x) for x in value])
            
            insort(records, (key,data))

        i = 0
        for key,data in records :
            self._dbh.put(key, data)

            i += 1
            if (0 == (i % 10000)) :
                time_last = time.time() - time_start
                logging(' - test %d times last %.2f seconds ...', i, time_last)
                
        time_last = time.time() - time_start
        tps = times / time_last
        logging('test %d times last %.2f seconds, tps=%.2f', times, time_last, tps)
        ## test03_tps()

    def test04_dupkey(self) :

        key = 'hello'
        # column : timestamp, ts, op, fileno, offset, length
        value = [ 12345678, 0, 0, 0, 5 ]
        data = ','.join([str(x) for x in value])
        self._dbh.put(key, data)

        key = 'hello'
        # column : timestamp, ts, op, fileno, offset, length
        value = [ 12345678, 0, 0, 5, 5 ]
        data = ','.join([str(x) for x in value])
        self._dbh.put(key, data)

        key = 'hello'
        # column : timestamp, ts, op, fileno, offset, length
        value = [ 12345678, 0, 0, 10, 5 ]
        data = ','.join([str(x) for x in value])
        self._dbh.put(key, data)

        _data = self._dbh.get( key )
        if data != _data :
            print 'GOT %s, WANT %s' % (repr(_data), repr(data))
        #assert data == _data

        c = self._dbh.cursor()
        print c
        r = c.set( key )
        print r
        print 'count:',c.count()
        while r :
            print r
            r = c.next()
        print r
        print 'count:',c.count()
        c.close()
        c = None

        c = self._dbh.cursor()
        r = c.set( 'key-not-found' )
        print r
        assert not r
        # bsddb.db.DBInvalidArgError: (22, 'Invalid argument -- Cursor position must be set before performing this operation')
        #print 'count:',c.count()
        #assert c.count() == 0
        c.close()
        c = None

        print 'test cursor performance ...'
        time_start = time.time()
        times = 10000
        for i in xrange(times) :
            c = self._dbh.cursor()
            r = c.set(key)
            assert c.count() > 0
            while r :
                r = c.next()
            c.close()
            c = None
        time_last = time.time() - time_start
        tps = times / time_last
        print ' - cursor %d times last %.2f seconds, tps=%.2f' % (times, time_last, tps)

        print 'test cursor performance with decode ...'
        time_start = time.time()
        times = 10000
        for i in xrange(times) :
            c = self._dbh.cursor()
            r = c.set(key)
            assert c.count() > 0
            while r :
                k,v = r
                t = v.split(',')
                x = [ int(x) for x in t ]

                r = c.next()
            c.close()
            c = None
        time_last = time.time() - time_start
        tps = times / time_last
        print ' - cursor %d times last %.2f seconds, tps=%.2f' % (times, time_last, tps)

        self._dbh.delete(key)

        _data = self._dbh.get( key )
        assert not _data

        ## test04_dupkey()


    ## class TestDBTables_2


if  __name__ == '__main__' :
    t = TestDBTables_2()

    t.run()


